0. Introduction

The purpose of this analysis is to determine which gene sets from MSigDB are most enriched in the genes we previously predicted to contain IREs. This is important as we wanted to see the biological relevance of the IRE-containing genes, and also their overlap in existing gene sets.

The approach is as follows:

The motivation for doing this analysis comes from an interesting observation using the naive approach of searching MSigDB for all gene sets with iron or heme in their name and seeing the overlap in genes between these gene sets and the IRE-containing genesets we identified.

ironSets <- list(
  c5_GO_2_IRON_2_SULFUR_CLUSTER_BINDING = c5_mapped$GO_2_IRON_2_SULFUR_CLUSTER_BINDING,
  c5_GO_4_IRON_4_SULFUR_CLUSTER_BINDING = c5_mapped$GO_4_IRON_4_SULFUR_CLUSTER_BINDING,
  c5_GO_CELLULAR_IRON_ION_HOMEOSTASIS = c5_mapped$GO_CELLULAR_IRON_ION_HOMEOSTASIS,
  c5_GO_HEME_METABOLIC_PROCESS = c5_mapped$GO_HEME_METABOLIC_PROCESS,
  c5_GO_HEME_BIOSYNTHETIC_PROCESS = c5_mapped$GO_HEME_BIOSYNTHETIC_PROCESS,
  c5_GO_HEMOGLOBIN_COMPLEX = c5_mapped$GO_HEMOGLOBIN_COMPLEX,
  c5_GO_IRON_COORDINATION_ENTITY_TRANSPORT = c5_mapped$GO_IRON_COORDINATION_ENTITY_TRANSPORT,
  c5_GO_IRON_ION_BINDING = c5_mapped$GO_IRON_ION_BINDING,
  c5_GO_IRON_ION_HOMEOSTASIS = c5_mapped$GO_IRON_ION_HOMEOSTASIS,
  c5_GO_IRON_ION_IMPORT = c5_mapped$GO_IRON_ION_IMPORT,
  c5_GO_IRON_ION_TRANSPORT = c5_mapped$GO_IRON_ION_TRANSPORT,
  c5_GO_OXIDOREDUCTASE_ACTIVITY_ACTING_ON_A_HEME_GROUP_OF_DONORS = c5_mapped$GO_OXIDOREDUCTASE_ACTIVITY_ACTING_ON_A_HEME_GROUP_OF_DONORS,
  c5_GO_RESPONSE_TO_IRON_ION = c5_mapped$GO_RESPONSE_TO_IRON_ION,
  h_HEME_METABOLISM = h_mapped$HALLMARK_HEME_METABOLISM,
  c2_REACTOME_IRON_UPTAKE_AND_TRANSPORT = c2_mapped$REACTOME_IRON_UPTAKE_AND_TRANSPORT
)
ironSets_idx <- ids2indices(ironSets, rownames(v))
head(ironSets,2)
$c5_GO_2_IRON_2_SULFUR_CLUSTER_BINDING
 [1] "ENSDARG00000062780" "ENSDARG00000079516" "ENSDARG00000052703" "ENSDARG00000013044" "ENSDARG00000007745" "ENSDARG00000038154"
 [7] "ENSDARG00000051956" "ENSDARG00000052190" "ENSDARG00000003462" "ENSDARG00000045733" "ENSDARG00000043665" "ENSDARG00000026582"
[13] "ENSDARG00000035596" "ENSDARG00000020054" "ENSDARG00000074356" "ENSDARG00000028546" "ENSDARG00000092713" "ENSDARG00000058725"
[19] "ENSDARG00000042159" "ENSDARG00000055240"

$c5_GO_4_IRON_4_SULFUR_CLUSTER_BINDING
 [1] "ENSDARG00000055472" "ENSDARG00000021466" "ENSDARG00000074552" "ENSDARG00000010267" "ENSDARG00000086853" "ENSDARG00000042727"
 [7] "ENSDARG00000062216" "ENSDARG00000027689" "ENSDARG00000026376" "ENSDARG00000036438" "ENSDARG00000074889" "ENSDARG00000004952"
[13] "ENSDARG00000035074" "ENSDARG00000052721" "ENSDARG00000051986" "ENSDARG00000092713" "ENSDARG00000077698" "ENSDARG00000021985"
[19] "ENSDARG00000104848" "ENSDARG00000110572" "ENSDARG00000042881" "ENSDARG00000004517" "ENSDARG00000062987" "ENSDARG00000054821"
[25] "ENSDARG00000038154" "ENSDARG00000051956" "ENSDARG00000011072" "ENSDARG00000055653" "ENSDARG00000058801" "ENSDARG00000007526"
[31] "ENSDARG00000026582" "ENSDARG00000035596" "ENSDARG00000058533" "ENSDARG00000022845" "ENSDARG00000038834" "ENSDARG00000028546"
[37] "ENSDARG00000045308" "ENSDARG00000007294" "ENSDARG00000078479" "ENSDARG00000074410"
head(ironSets_idx,2)
$c5_GO_2_IRON_2_SULFUR_CLUSTER_BINDING
 [1]   392  1152  2009  3261  4076  4317  5266  5874  6672  7030  7494  7669  7725  7829  9213 10372 11992 13712

$c5_GO_4_IRON_4_SULFUR_CLUSTER_BINDING
 [1]   582   666  1075  1117  1553  1678  3513  3591  3669  4053  4076  4219  4317  5167  5266  5472  5874  5996  6790  6835  7375
[22]  7669  7675  7834  8248  8392  8444  9159  9231 10196 10441 12008 12054 12192 13108 13371 14022 18609

We used a gene set enrichment analysis approach to test for whether these iron-related gene sets were enriched in our DE results.

source(here("R","GSEA","combinedGSEA.R"))

gseaResults_ironSets <- combinedGSEA(v, ironSets_idx, design, contrasts)
# gseaResults_ironSets %>% saveRDS(here("R","GSEA","results","gseaResults_ironSets.rds"))

Results for 6 month, normoxia, mutant vs. wild type below indicate that while some iron-related gene sets show enrichment in this comparison, others do not.

gseaResults_ironSets$combTest$normoxia_6mth_mutant_vs_wt

Overall, the figure below indicates poor overlap between existing gene sets and our IRE-containing gene sets. However, to really see how our IRE genesets relate to existing gene sets, we need to do a more comprehensive test including more of the gene sets not restricted to ones related to iron metabolism.

# Create a dataframe that includes overlap between iron-related gene sets and the ireGenes.
testx <- ironSets %>% lapply(function(x){
  overlap <- list(
  with3ire = sum(x %in% ireGenes$ire3_all),
  with5ire = sum(x %in% ireGenes$ire5_all),
  noIre = sum(!(x %in% ireGenes$ire3_all | x %in% ireGenes$ire5_all))
  )
}) %>% lapply(function(x){
    x %>% bind_rows
}) %>% 
  do.call("rbind",.) %>% 
  mutate(geneset = names(ironSets)) %>%
  bind_rows(data.frame(
    with3ire = length(ireGenes$ire3_all),
    with5ire = length(ireGenes$ire5_all),
    noIre = 0,
    geneset = "- GRCz11 Zebrafish Genes - All Predicted IREs"
  )) %>% 
  bind_rows(data.frame(
    with3ire = ireUtr3 %>% as.data.frame %>% dplyr::filter(quality == "High") %>% use_series("gene_id") %>% unique %>% length,
    with5ire = ireUtr5 %>% as.data.frame %>% dplyr::filter(quality == "High") %>% use_series("gene_id") %>% unique %>% length,
    noIre = 0,
    geneset = "- GRCz11 Zebrafish Genes - High Quality Predicted IREs"
  )) %>%
  mutate(geneset = gsub(x = geneset, pattern = "_", replacement = " "))%>%
  melt 
binding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorUsing geneset as id variables
# Create a stacked bar chart
compPlot <- testx %>% ggplot(aes(x = str_to_title(stringr::str_wrap(geneset, 20)), 
                     y = value, 
                     fill = variable)) + 
  geom_col() +
  theme(aspect.ratio = 0.7, 
        axis.text.x =  element_text(size= 35), 
        axis.title.x = element_text(size= 30),
        axis.title.y = element_text(size = 30), 
        legend.text = element_text(size = 20), 
        legend.title = element_text(size=30),
        axis.text.y = element_text(color = "grey20", size = 16)) + 
  coord_flip() +  # Axis labels too long to be readable so this helps.
  scale_fill_manual(values=c("#FBB829", "#FF0066", "#dddddd"), labels =c("3' IRE", "5' IRE", "No IRE")) +
  labs(y = "Number of genes", x = "Gene set", fill = "Gene\nhas\nIRE?") 
compPlot

1. Defining Relevant Gene Sets

We will be testing whether our IRE gene sets are enriched in any of the gene sets in the following gene set collections from MSigDB:

The gene set collections mapped to zebrafish Ensembl IDs have been pre-loaded for this analysis. Each collection is a named list of gene sets, with each gene set containing Ensembl IDs of genes in that set. We will convert all lists into list column structures used in the tibble package.

h_tib <- h_mapped %>% tibble %>% set_colnames(c("ids")) %>% mutate(geneset = names(h_mapped), source = "h") 
c2_tib <- c2_mapped %>% tibble %>% set_colnames(c("ids")) %>% mutate(geneset = names(c2_mapped), source = "c2")
c3_tib <- c3_mapped %>% tibble %>% set_colnames(c("ids")) %>% mutate(geneset = names(c3_mapped), source = "c3")
c5_tib <- c5_mapped %>% tibble %>% set_colnames(c("ids")) %>% mutate(geneset = names(c5_mapped), source = "c5")
gs <- bind_rows(h_tib, c2_tib, c3_tib, c5_tib)
gs

We previously loaded the ireGenes R object, which has the following structure:

ireGenes%>%str
List of 4
 $ ire3_all: chr [1:1207] "ENSDARG00000102878" "ENSDARG00000070477" "ENSDARG00000006038" "ENSDARG00000063481" ...
 $ ire5_all: chr [1:393] "ENSDARG00000045911" "ENSDARG00000017386" "ENSDARG00000045886" "ENSDARG00000015921" ...
 $ ire3_hq : chr [1:106] "ENSDARG00000074223" "ENSDARG00000024874" "ENSDARG00000110878" "ENSDARG00000089296" ...
 $ ire5_hq : chr [1:49] "ENSDARG00000045911" "ENSDARG00000101351" "ENSDARG00000100006" "ENSDARG00000015551" ...
allIREGenes <- c(ireGenes$ire3_all, ireGenes$ire5_all)
allIREGenes %>% head
[1] "ENSDARG00000102878" "ENSDARG00000070477" "ENSDARG00000006038" "ENSDARG00000063481" "ENSDARG00000045842" "ENSDARG00000095332"

2. Compute gene set overlap with IRE genesets

We wish to see the overlap between the genesets in gs with the ireGenes (including 3’ and 5’ IRE genes), as well as separately, with the 3’ IRE genes and 5’ IRE genes. We will add this information into the gs tibble.

gs %<>% rowwise() %>% mutate(
  n = length(ids),  # Number of genes in the geneset
  
  n_with_ire = sum(ids %in% allIREGenes),  # Number of genes in the gene set which have 3' or 5' predicted IREs 
  n_without_ire = (n - n_with_ire),
  universe_with_ire = sum(rownames(v) %in% allIREGenes & !(rownames(v) %in% ids)), 
  universe_without_ire = (length(rownames(v)) - universe_with_ire),
  
  n_with_ire3 = sum(ids %in% ireGenes$ire3_all),  # Number of genes in the gene set which have 3' predicted IREs
  n_without_ire3= (n - n_with_ire),
  universe_with_ire3 = sum(rownames(v) %in% ireGenes$ire3_all & !(rownames(v) %in% ids)), 
  universe_without_ire3 = (length(rownames(v)) - universe_with_ire),
  
  n_with_ire5 = sum(ids %in% ireGenes$ire5_all),  # Number of genes in the gene set which have 5' predicted IREs
  n_without_ire5= (n - n_with_ire),
  universe_with_ire5 = sum(rownames(v) %in% ireGenes$ire5_all & !(rownames(v) %in% ids)), 
  universe_without_ire5 = (length(rownames(v)) - universe_with_ire)
) %>% ungroup()
gs

3. Contingency tables

Create contingency table for each gene set. We will store this in a list of matrices gs_mat. The 3’ and 5’ contingency matrices are stored in gs_mat3 and gs_mat5.

gs_mat <- gs %>% 
  dplyr::select(n_with_ire,
                n_without_ire, 
                universe_with_ire, 
                universe_without_ire) %>% 
  apply(X = ., MARGIN = 1, FUN = function(x){
    x %>% 
      matrix(2,2) %>% 
      t %>% 
      list()
  }) %>% set_names(gs$geneset) %>% 
  lapply(function(x){
    x %>% .[[1]]
  })

gs_mat3 <- gs %>% 
  dplyr::select(n_with_ire3,
                n_without_ire3, 
                universe_with_ire3, 
                universe_without_ire3) %>% 
  apply(X = ., MARGIN = 1, FUN = function(x){
    x %>% 
      matrix(2,2) %>% 
      t %>% 
      list()
  }) %>% set_names(gs$geneset) %>% 
  lapply(function(x){
    x %>% .[[1]]
  })

gs_mat5 <- gs %>% 
  dplyr::select(n_with_ire5,
                n_without_ire5, 
                universe_with_ire5, 
                universe_without_ire5) %>% 
  apply(X = ., MARGIN = 1, FUN = function(x){
    x %>% 
      matrix(2,2) %>% 
      t %>% 
      list()
  }) %>% set_names(gs$geneset) %>% 
  lapply(function(x){
    x %>% .[[1]]
  })
gs_mat[1:3]
$HALLMARK_TNFA_SIGNALING_VIA_NFKB
     [,1]  [,2]
[1,]   15   243
[2,] 1433 18274

$HALLMARK_HYPOXIA
     [,1]  [,2]
[1,]   19   241
[2,] 1430 18277

$HALLMARK_CHOLESTEROL_HOMEOSTASIS
     [,1]  [,2]
[1,]    5    86
[2,] 1443 18264
gs_mat3[1:3]
$HALLMARK_TNFA_SIGNALING_VIA_NFKB
     [,1]  [,2]
[1,]    9   243
[2,] 1119 18274

$HALLMARK_HYPOXIA
     [,1]  [,2]
[1,]   14   241
[2,] 1115 18277

$HALLMARK_CHOLESTEROL_HOMEOSTASIS
     [,1]  [,2]
[1,]    4    86
[2,] 1124 18264
gs_mat5[1:3]
$HALLMARK_TNFA_SIGNALING_VIA_NFKB
     [,1]  [,2]
[1,]    6   243
[2,]  344 18274

$HALLMARK_HYPOXIA
     [,1]  [,2]
[1,]    7   241
[2,]  343 18277

$HALLMARK_CHOLESTEROL_HOMEOSTASIS
     [,1]  [,2]
[1,]    2    86
[2,]  348 18264

4. Fisher’s exact test

On each contingency table, we will run Fisher’s exact test. We then apply FDR correction to adjust the raw p-values for multiple testing.

fisher_res <- gs_mat %>% lapply(function(x){
  x %>% fisher.test()
})

fisher_res3 <- gs_mat3 %>% lapply(function(x){
  x %>% fisher.test()
})

fisher_res5 <- gs_mat5 %>% lapply(function(x){
  x %>% fisher.test()
})

fisher_res_p <- fisher_res %>% lapply(function(x){x$p.value})
fisher_res_p3 <- fisher_res3 %>% lapply(function(x){x$p.value})
fisher_res_p5 <- fisher_res5 %>% lapply(function(x){x$p.value})

gs %<>% 
  mutate(fisher_p = fisher_res_p%>%unlist%>%unname,
         fisher_p_3 = fisher_res_p3%>%unlist%>%unname,
         fisher_p_5 = fisher_res_p5%>%unlist%>%unname) %>%
  mutate(fdr = p.adjust(fisher_p, "fdr"),
         fdr_3 = p.adjust(fisher_p_3, "fdr"),
         fdr_5 = p.adjust(fisher_p_5, "fdr"))

5. Calculate Expected and Observed

For each gene set, we will calculate the number of genes expected to have IREs (based on the background proportion) and observed value. This information will be added to the gs object.

gs %<>% rowwise() %>% mutate(
  exp_allIRE = (universe_with_ire / universe_without_ire)*n_without_ire,
  obs_allIRE = n_with_ire,
  obs_greater_than_exp_allIRE = obs_allIRE > exp_allIRE,
  
  exp_ire3 = (universe_with_ire3 / universe_without_ire3)*n_without_ire3,
  obs_ire3 = n_with_ire3,
  obs_greater_than_exp_ire3 = obs_ire3 > exp_ire3,
  
  exp_ire5 = (universe_with_ire5 / universe_without_ire5)*n_without_ire5,
  obs_ire5 = n_with_ire5,
  obs_greater_than_exp_ire5 = obs_ire5 > exp_ire5
) 
gs %>%
  dplyr::select(geneset, contains("exp"), starts_with("obs"), ) %>% ungroup()

6. Results

The following table shows the top 50 gene sets enriched in IRE genesets, ranked by Fisher’s exact test p-values:

gs %>% ungroup() %>%
  arrange(fisher_p) %>%
  dplyr::select(geneset, contains("fdr"), n_with_ire, n, starts_with("obs_greater")) %>% 
  head(50)

Sorted by genesets most enriched in 3’ IRE genes:

gs %>% ungroup %>% arrange(fisher_p_3) %>% dplyr::select(geneset, contains("fdr"), n_with_ire3, n, starts_with("obs_greater")) %>% head(50)

Sorted by genesets most enriched in 5’ IRE genes:

gs %>% ungroup %>% arrange(fisher_p_5) %>% dplyr::select(geneset, contains("fdr"), n_with_ire5, n, starts_with("obs_greater")) %>% head(50)

7. Visualisation (Stacked bar chart)

The following stacked bar chart shows the overlap between the top ~20 genesets and the predicted-IRE genesets.

overlapDf <- gs %>% arrange(fisher_p) %>% dplyr::filter(fdr < 0.1 | fdr_3 < 0.1 | fdr_5 < 0.1) %>%
  dplyr::filter(obs_greater_than_exp_allIRE == TRUE | obs_greater_than_exp_ire3 == TRUE | obs_greater_than_exp_ire5 == TRUE) %>%
  dplyr::select(geneset, source, n_with_ire3, n_with_ire5, n_without_ire) %>%
  bind_rows(data.frame(
    geneset = c("- GRCz11 Zebrafish Genes - All Predicted IREs"),
    source = c("sires"),
    n_with_ire3 = c(length(ireGenes$ire3_all)),
#                    ireUtr3 %>% as.data.frame %>% dplyr::filter(quality == "High") %>% use_series("gene_id") %>% unique %>% length),
    n_with_ire5 = c(length(ireGenes$ire5_all)),
#                    ireUtr5 %>% as.data.frame %>% dplyr::filter(quality == "High") %>% use_series("gene_id") %>% unique %>% length),
    n_without_ire = c(0)
  )) %>% dplyr::mutate(geneset = paste0(geneset, " ", source)) %>%
  dplyr::mutate(geneset = gsub(x=geneset, pattern = "_", replacement = " ")) %>%
  dplyr::select(-source) %>% melt
binding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorUsing geneset as id variables
overlapPlot <- overlapDf %>% ggplot(aes(x = str_to_title(stringr::str_wrap(geneset, 23)), 
                     y = value, 
                     fill = variable)) + 
  geom_col() +
  theme(aspect.ratio = 0.7, 
        axis.text.x =  element_text(size= 35), 
        axis.title.x = element_text(size= 30),
        axis.title.y = element_text(size = 30), 
        legend.text = element_text(size = 20), 
        legend.title = element_text(size=30),
        axis.text.y = element_text(color = "grey20", size = 13)) + 
  coord_flip() +  # Axis labels too long to be readable so this helps.
  scale_fill_manual(values=c("#FBB829", "#FF0066", "#dddddd"), labels =c("3' IRE", "5' IRE", "No IRE")) +
  labs(y = "Number of genes", x = "Gene set", fill = "Gene\nhas\nIRE?") 
overlapPlot

# export::graph2ppt(overlapPlot, here("R","GSEA","fig","overlapPlot"))

8. Visualisation (Network)

Although the stacked bar chart shows overlap between different gene sets with the predicted IRE geneset, along with information about the size of each geneset, crucially, it doesn’t indicate which genes are in common between the different gene sets. My first idea was pairwise Venn diagrams between the IRE genes in each pair of genesets, but this isn’t the most visual / intuitive way to present the information and it doesn’t highlight the genes which are most often shared between different genesets. Steve had the idea to represent this as a network, with distinct groups of genes (grey) representing genesets, IRE genes in colour, and edges representing overlap between genesets.

8.1. Create the nodes table

I will need to create a data.frame with the following columns:

  • Id: Gene ID
  • IRE: Factor which can either be 3' IRE, 5' IRE, or no IRE.
  • Geneset: The gene set which it belongs to.
nodes<- gs %>% arrange(fisher_p) %>% dplyr::filter(fdr < 0.1 | fdr_3 < 0.1 | fdr_5 < 0.1) %>%
  dplyr::filter(obs_greater_than_exp_allIRE == TRUE | obs_greater_than_exp_ire3 == TRUE | obs_greater_than_exp_ire5 == TRUE) %>%
  ungroup
# Append information about 3' and 5' IRE genesets 
nodes %<>% bind_rows(
   tibble(
     ids = c(list(ireGenes$ire3_all, ireGenes$ire5_all)),
     geneset = c("Predicted 3' IRE genes", "Predicted 5' IRE genes"),
     source = c("sires","sires"),
     n = c(length(ireGenes$ire3_all), length(ireGenes$ire5_all))
   )
 ) 
# Append the Hallmark Heme Metabolism geneset
# Although this gene set wasnt significantly enriched in 
# IRE-containing genes, it is probably the most comprehensive
# gene set specifically on heme metabolism, and combines info 
# from various studies. 
nodes %<>% bind_rows(
  h_tib %>% filter(geneset == "HALLMARK_HEME_METABOLISM") %>% mutate(n = length(ids[[1]]))
 ) 
nodes

First we filtered gs for only the genesets that are highly ranked in being enriched for predicted IRE genes. We applied the additional filtering step that the observed number of IRE genes is greater than the expected number. This results in 18 genesets.

The next step is to create all possible combinations of the genesets. We will use the combn function to do this. Then we will create a column common_ids to store the genes which are common to both gene sets being intersected.

gsComb <- combn(nodes$geneset, m = 2) %>% t %>% as.data.frame %>%
  set_colnames(c("geneset", "geneset_2b")) %>%
  left_join(nodes%>%dplyr::select(ids, geneset), by = "geneset") %>%
  dplyr::rename(geneset_nm = geneset,
                geneset = geneset_2b) %>%
  left_join(nodes%>%dplyr::select(ids, geneset), by = "geneset") %>%
  as_tibble %>%
  mutate(common_ids = map2(ids.x, ids.y, ~intersect(.x,.y)))
Column `geneset` joining factor and character vector, coercing into character vectorColumn `geneset` joining factor and character vector, coercing into character vector
gsComb 

We can extract genes which appear the most often in genesets as follows:

gsComb$common_ids%>%unlist %>% table %>% as.data.frame%>% arrange(desc(Freq)) %>% set_colnames(c("ensembl_gene_id", "f")) %>% left_join(v$genes %>% dplyr::select(-entrezid)) %>% as_tibble
Joining, by = "ensembl_gene_id"
Column `ensembl_gene_id` joining factor and character vector, coercing into character vector

The nodes table will contain

  • All genes in the genesets
  • The geneset names
pathways <- nodes$geneset %>% as.data.frame %>% set_colnames("label")
genes <- nodes$ids %>% unlist %>% unique %>% as.data.frame %>% set_colnames("label")
nodesDf <- full_join(pathways, genes, by = "label") %>% rowid_to_column("id")
Column `label` joining factors with different levels, coercing to character vector
# nodesDf %>%
#   mutate(text = ifelse(id < 18, label, NA)) %>%
#   mutate(size = ifelse(id < 16, 2, 1)) %>%
#   mutate(colour = ifelse(id < 16, rainbow(26)[id], NA)) 
head(nodesDf,20)
   id                                                             label
1   1                                                TTGCWCAAY_CEBPB_02
2   2                                                     TGANTCA_AP1_C
3   3                                               YRTCANNRCGC_UNKNOWN
4   4                                                 TGGAAA_NFAT_Q4_01
5   5                                                       KEGG_GLIOMA
6   6                                      PID_BETA_CATENIN_NUC_PATHWAY
7   7                                  DACOSTA_UV_RESPONSE_VIA_ERCC3_DN
8   8                                                   WTGAAAT_UNKNOWN
9   9                              GO_INTRACELLULAR_SIGNAL_TRANSDUCTION
10 10                                                    CTACTAG_MIR325
11 11                                            TGGNNNNNNKCCAR_UNKNOWN
12 12                                          GO_POSTSYNAPTIC_MEMBRANE
13 13                                              GO_SYNAPTIC_MEMBRANE
14 14 GO_POSITIVE_REGULATION_OF_BLOOD_VESSEL_ENDOTHELIAL_CELL_MIGRATION
15 15                                               TNCATNTCCYR_UNKNOWN
16 16                                            Predicted 3' IRE genes
17 17                                            Predicted 5' IRE genes
18 18                                          HALLMARK_HEME_METABOLISM
19 19                                                ENSDARG00000009418
20 20                                                ENSDARG00000029764
nodesDf2 <- nodesDf %>%
  mutate(
    ire = case_when(
      id < 16 ~ " ",
      id == 18 ~ " ",
      id == 16 ~ "3",
      id == 17 ~ "5",
      label %in% ireGenes$ire3_all ~ "3",
      label %in% ireGenes$ire5_all ~ "5",
      !(label %in% allIREGenes) ~ "no IRE"
    )
  )%>%
  dplyr::select(-id) %>% dplyr::rename(Id = label) 
head(nodesDf2, 30) 
                                                                  Id    ire
1                                                 TTGCWCAAY_CEBPB_02       
2                                                      TGANTCA_AP1_C       
3                                                YRTCANNRCGC_UNKNOWN       
4                                                  TGGAAA_NFAT_Q4_01       
5                                                        KEGG_GLIOMA       
6                                       PID_BETA_CATENIN_NUC_PATHWAY       
7                                   DACOSTA_UV_RESPONSE_VIA_ERCC3_DN       
8                                                    WTGAAAT_UNKNOWN       
9                               GO_INTRACELLULAR_SIGNAL_TRANSDUCTION       
10                                                    CTACTAG_MIR325       
11                                            TGGNNNNNNKCCAR_UNKNOWN       
12                                          GO_POSTSYNAPTIC_MEMBRANE       
13                                              GO_SYNAPTIC_MEMBRANE       
14 GO_POSITIVE_REGULATION_OF_BLOOD_VESSEL_ENDOTHELIAL_CELL_MIGRATION       
15                                               TNCATNTCCYR_UNKNOWN       
16                                            Predicted 3' IRE genes      3
17                                            Predicted 5' IRE genes      5
18                                          HALLMARK_HEME_METABOLISM       
19                                                ENSDARG00000009418      5
20                                                ENSDARG00000029764 no IRE
21                                                ENSDARG00000077842 no IRE
22                                                ENSDARG00000040184 no IRE
23                                                ENSDARG00000026723 no IRE
24                                                ENSDARG00000033498 no IRE
25                                                ENSDARG00000071168 no IRE
26                                                ENSDARG00000069356 no IRE
27                                                ENSDARG00000075899 no IRE
28                                                ENSDARG00000111712 no IRE
29                                                ENSDARG00000090624 no IRE
30                                                ENSDARG00000055543 no IRE
# nodesDf2 %>% write_tsv(here("R","GSEA","data","nodes2.tsv"))

Edges will be between:

  • The genesets and all genes within the geneset
  • Genes in multiple genesets
edgeDf <- nodes$ids %>% set_names(nodes$geneset) %>% plyr::ldply(data.frame) %>% 
  set_colnames(c("pathway", "gene")) %>%
  left_join(nodesDf, by = c("pathway"="label")) %>%
  dplyr::rename(from = id) %>%
  left_join(nodesDf, by = c("gene"="label")) %>%
  dplyr::rename(to=id) %>%
  #dplyr::select(from, to)
  dplyr::select(pathway, gene) %>%
  set_colnames(c("Source","Target"))
Column `gene`/`label` joining factor and character vector, coercing into character vector
edgeDf %>% head(20)
               Source             Target
1  TTGCWCAAY_CEBPB_02 ENSDARG00000009418
2  TTGCWCAAY_CEBPB_02 ENSDARG00000029764
3  TTGCWCAAY_CEBPB_02 ENSDARG00000077842
4  TTGCWCAAY_CEBPB_02 ENSDARG00000040184
5  TTGCWCAAY_CEBPB_02 ENSDARG00000026723
6  TTGCWCAAY_CEBPB_02 ENSDARG00000033498
7  TTGCWCAAY_CEBPB_02 ENSDARG00000071168
8  TTGCWCAAY_CEBPB_02 ENSDARG00000069356
9  TTGCWCAAY_CEBPB_02 ENSDARG00000075899
10 TTGCWCAAY_CEBPB_02 ENSDARG00000111712
11 TTGCWCAAY_CEBPB_02 ENSDARG00000090624
12 TTGCWCAAY_CEBPB_02 ENSDARG00000055543
13 TTGCWCAAY_CEBPB_02 ENSDARG00000104279
14 TTGCWCAAY_CEBPB_02 ENSDARG00000005416
15 TTGCWCAAY_CEBPB_02 ENSDARG00000077948
16 TTGCWCAAY_CEBPB_02 ENSDARG00000077749
17 TTGCWCAAY_CEBPB_02 ENSDARG00000051783
18 TTGCWCAAY_CEBPB_02 ENSDARG00000087517
19 TTGCWCAAY_CEBPB_02 ENSDARG00000014181
20 TTGCWCAAY_CEBPB_02 ENSDARG00000004843

Export results

gs %>% saveRDS(here("R","GSEA","results","gs.rds"))
gs %>% ungroup() %>% write.xlsx(here("R","GSEA","results","gs.xlsx"))

# Export significant genesets / of interest
gs %>%ungroup %>% 
  arrange(fisher_p) %>%
  dplyr::filter(fdr < 0.1 | fdr_3 < 0.1 | fdr_5 < 0.1) %>%
  dplyr::filter(obs_greater_than_exp_allIRE == TRUE | obs_greater_than_exp_ire3 == TRUE | obs_greater_than_exp_ire5 == TRUE) %>%
  dplyr::select(geneset, source, n, n_with_ire3, n_with_ire5, n_without_ire, contains("fisher_p"), contains("fdr")) %>%
  write.xlsx(here("R","GSEA","results","gs_topRanked.xlsx"))

9. Human Data

zebrafishIreGenes <- readRDS(here::here("R/GSEA/data/ireGenes.rds"))
humanIreGenes <- readRDS(here::here("R/IREGenes/data/human_ireGenes.rds"))

TODO

Session Info

sessionInfo()
LS0tCnRpdGxlOiAiQXJlIElSRSBnZW5lIHNldHMgb3Zlci1yZXByZXNlbnRlZCBpbiBNU2lnREIgZ2VuZSBzZXRzPyIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKLS0tCgpgYGB7ciBTZXR1cCwgaW5jbHVkZT1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBMb2FkIHBhY2thZ2VzCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocmVhZHIpCmxpYnJhcnkobWFncml0dHIpCmxpYnJhcnkobGltbWEpCmxpYnJhcnkoaGVyZSkKbGlicmFyeShvcGVueGxzeCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkocHVycnIpCmxpYnJhcnkoZXhwb3J0KQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KGZnc2VhKQpsaWJyYXJ5KHRpYmJsZSkKCiMgZ2dwbG90MiB0aGVtZQp0aGVtZV9zZXQodGhlbWVfYncoKSkKCiMgTG9hZCBvYmplY3RzIHJlcXVpcmVkIGZvciB0aGlzIGFuYWx5c2lzLiAKIyBHZW5lIHNldCBjb2xsZWN0aW9ucyB3aXRoIGh1bWFuIGVudHJlemdlbmVzIGNvbnZlcnRlZCB0byB6ZWJyYWZpc2ggRW5zZW1ibCBJRHMgYW5kIHNhdmVkIGFzIGxpc3RzIG9mIGdlbmUgc2V0cy4gCmhfbWFwcGVkIDwtIHJlYWRSRFMoaGVyZSgiUiIsIkdTRUEiLCJkYXRhIiwiZW5zX2hfbWFwcGVkLnJkcyIpKSAgIyBIYWxsbWFyayBHZW5lIHNldCBjb2xsZWN0aW9uIApjMl9tYXBwZWQgPC0gcmVhZFJEUyhoZXJlKCJSIiwiR1NFQSIsImRhdGEiLCJlbnNfYzJfbWFwcGVkLnJkcyIpKSAgIyBDdXJhdGVkIEdlbmUgc2V0IGNvbGxlY3Rpb24KYzNfbWFwcGVkIDwtIHJlYWRSRFMoaGVyZSgiUiIsIkdTRUEiLCJkYXRhIiwiZW5zX2MzX21hcHBlZC5yZHMiKSkgICMgTW90aWYgR2VuZSBzZXQgY29sbGVjdGlvbgpjNV9tYXBwZWQgPC0gcmVhZFJEUyhoZXJlKCJSIiwiR1NFQSIsImRhdGEiLCJlbnNfYzVfbWFwcGVkLnJkcyIpKSAgIyBHZW5lIE9udG9sb2d5IEdlbmUgc2V0IGNvbGxlY3Rpb24gCmlyZUdlbmVzIDwtIHJlYWRSRFMoaGVyZSgiUiIsIkdTRUEiLCJkYXRhIiwiaXJlR2VuZXMucmRzIikpICMgTGlzdCBvZiBJUkUgZ2VuZSBzZXRzLCBjb250YWlucyA0IGdlbmUgc2V0czogMycgQWxsIElSRSBnZW5lcywgNScgQWxsIElSRSBnZW5lcywgMycgSGlnaC1RdWFsaXR5IElSRSBnZW5lcywgNScgSGlnaC1RdWFsaXR5IElSRSBnZW5lcyAKCiMgSVJFIGdlbmVzIEdSYW5nZXMgb2JqZWN0cyBleHBvcnRlZCBmcm9tIFNJUkVzIHdlYiBzZXJ2ZXIKaXJlVXRyMyA8LSByZWFkUkRTKGhlcmUoIlIiLCJJUkVHZW5lcyIsImRhdGEiLCJpcmVVdHIzLnJkcyIpKQppcmVVdHI1IDwtIHJlYWRSRFMoaGVyZSgiUiIsIklSRUdlbmVzIiwiZGF0YSIsImlyZVV0cjUucmRzIikpCgojIERFIGFuYWx5c2lzIG9iamVjdHMKdiA8LSByZWFkUkRTKGhlcmUoIlIiLCJERSIsImRhdGEiLCJ2b29tRGF0YV9nLnJkcyIpKSAgIyB2b29tIG9iamVjdApkZXNpZ24gPC0gcmVhZFJEUyhoZXJlKCJSIiwiREUiLCJkYXRhIiwiZGVzaWduX2cucmRzIikpICAjIERlc2lnbiBtYXRyaXgKY29udHJhc3RzIDwtIHJlYWRSRFMoaGVyZSgiUiIsIkRFIiwiZGF0YSIsImNvbnRyYXN0c19nLnJkcyIpKSAgIyBDb250cmFzdHMgbWF0cml4CgpgYGAKCgojIyAwLiBJbnRyb2R1Y3Rpb24KClRoZSBwdXJwb3NlIG9mIHRoaXMgYW5hbHlzaXMgaXMgdG8gZGV0ZXJtaW5lIHdoaWNoIGdlbmUgc2V0cyBmcm9tIFtNU2lnREJdKGh0dHA6Ly9zb2Z0d2FyZS5icm9hZGluc3RpdHV0ZS5vcmcvZ3NlYS9tc2lnZGIvaW5kZXguanNwKSAKYXJlIG1vc3QgZW5yaWNoZWQgaW4gdGhlIGdlbmVzIHdlIHByZXZpb3VzbHkgcHJlZGljdGVkIHRvIGNvbnRhaW4gSVJFcy4gVGhpcyBpcyBpbXBvcnRhbnQgYXMgd2Ugd2FudGVkIHRvIHNlZSB0aGUgYmlvbG9naWNhbCByZWxldmFuY2Ugb2YgdGhlIElSRS1jb250YWluaW5nIGdlbmVzLCBhbmQgYWxzbyB0aGVpciBvdmVybGFwIGluIGV4aXN0aW5nIGdlbmUgc2V0cy4gCgpUaGUgYXBwcm9hY2ggaXMgYXMgZm9sbG93czoKCi0gTG9hZCBpbiByZWxldmFudCBnZW5lIHNldCBjb2xsZWN0aW9ucyBmcm9tIE1TaWdEQi4KLSBHZXQgdGhlbSBpbnRvIGEgZm9ybWF0IHN1aXRhYmxlIGZvciBwZXJmb3JtaW5nIEZpc2hlcidzIGV4YWN0IHRlc3QgdG8gdGVzdCB3aGV0aGVyIHRoZXkgYXJlIGVucmljaGVkIGZvciBJUkUtY29udGFpbmluZyBnZW5lcy4gCi0gUmVzdWx0cyBkaXNwbGF5ZWQgb24gYSBiYXIgY2hhcnQgYW5kIGFsc28gYXMgYSBuZXR3b3JrLiAKClRoZSBtb3RpdmF0aW9uIGZvciBkb2luZyB0aGlzIGFuYWx5c2lzIGNvbWVzIGZyb20gYW4gaW50ZXJlc3Rpbmcgb2JzZXJ2YXRpb24gdXNpbmcgdGhlIG5haXZlIGFwcHJvYWNoIG9mIHNlYXJjaGluZyBNU2lnREIgZm9yIGFsbCBnZW5lIHNldHMgd2l0aCAqKmlyb24qKiBvciAqKmhlbWUqKiBpbiB0aGVpciBuYW1lIGFuZCBzZWVpbmcgdGhlIG92ZXJsYXAgaW4gZ2VuZXMgYmV0d2VlbiB0aGVzZSBnZW5lIHNldHMgYW5kIHRoZSBJUkUtY29udGFpbmluZyBnZW5lc2V0cyB3ZSBpZGVudGlmaWVkLiAKCmBgYHtyfQppcm9uU2V0cyA8LSBsaXN0KAogIGM1X0dPXzJfSVJPTl8yX1NVTEZVUl9DTFVTVEVSX0JJTkRJTkcgPSBjNV9tYXBwZWQkR09fMl9JUk9OXzJfU1VMRlVSX0NMVVNURVJfQklORElORywKICBjNV9HT180X0lST05fNF9TVUxGVVJfQ0xVU1RFUl9CSU5ESU5HID0gYzVfbWFwcGVkJEdPXzRfSVJPTl80X1NVTEZVUl9DTFVTVEVSX0JJTkRJTkcsCiAgYzVfR09fQ0VMTFVMQVJfSVJPTl9JT05fSE9NRU9TVEFTSVMgPSBjNV9tYXBwZWQkR09fQ0VMTFVMQVJfSVJPTl9JT05fSE9NRU9TVEFTSVMsCiAgYzVfR09fSEVNRV9NRVRBQk9MSUNfUFJPQ0VTUyA9IGM1X21hcHBlZCRHT19IRU1FX01FVEFCT0xJQ19QUk9DRVNTLAogIGM1X0dPX0hFTUVfQklPU1lOVEhFVElDX1BST0NFU1MgPSBjNV9tYXBwZWQkR09fSEVNRV9CSU9TWU5USEVUSUNfUFJPQ0VTUywKICBjNV9HT19IRU1PR0xPQklOX0NPTVBMRVggPSBjNV9tYXBwZWQkR09fSEVNT0dMT0JJTl9DT01QTEVYLAogIGM1X0dPX0lST05fQ09PUkRJTkFUSU9OX0VOVElUWV9UUkFOU1BPUlQgPSBjNV9tYXBwZWQkR09fSVJPTl9DT09SRElOQVRJT05fRU5USVRZX1RSQU5TUE9SVCwKICBjNV9HT19JUk9OX0lPTl9CSU5ESU5HID0gYzVfbWFwcGVkJEdPX0lST05fSU9OX0JJTkRJTkcsCiAgYzVfR09fSVJPTl9JT05fSE9NRU9TVEFTSVMgPSBjNV9tYXBwZWQkR09fSVJPTl9JT05fSE9NRU9TVEFTSVMsCiAgYzVfR09fSVJPTl9JT05fSU1QT1JUID0gYzVfbWFwcGVkJEdPX0lST05fSU9OX0lNUE9SVCwKICBjNV9HT19JUk9OX0lPTl9UUkFOU1BPUlQgPSBjNV9tYXBwZWQkR09fSVJPTl9JT05fVFJBTlNQT1JULAogIGM1X0dPX09YSURPUkVEVUNUQVNFX0FDVElWSVRZX0FDVElOR19PTl9BX0hFTUVfR1JPVVBfT0ZfRE9OT1JTID0gYzVfbWFwcGVkJEdPX09YSURPUkVEVUNUQVNFX0FDVElWSVRZX0FDVElOR19PTl9BX0hFTUVfR1JPVVBfT0ZfRE9OT1JTLAogIGM1X0dPX1JFU1BPTlNFX1RPX0lST05fSU9OID0gYzVfbWFwcGVkJEdPX1JFU1BPTlNFX1RPX0lST05fSU9OLAogIGhfSEVNRV9NRVRBQk9MSVNNID0gaF9tYXBwZWQkSEFMTE1BUktfSEVNRV9NRVRBQk9MSVNNLAogIGMyX1JFQUNUT01FX0lST05fVVBUQUtFX0FORF9UUkFOU1BPUlQgPSBjMl9tYXBwZWQkUkVBQ1RPTUVfSVJPTl9VUFRBS0VfQU5EX1RSQU5TUE9SVAopCgppcm9uU2V0c19pZHggPC0gaWRzMmluZGljZXMoaXJvblNldHMsIHJvd25hbWVzKHYpKQoKaGVhZChpcm9uU2V0cywyKQpoZWFkKGlyb25TZXRzX2lkeCwyKQpgYGAKCldlIHVzZWQgYSBnZW5lIHNldCBlbnJpY2htZW50IGFuYWx5c2lzIGFwcHJvYWNoIHRvIHRlc3QgZm9yIHdoZXRoZXIgdGhlc2UgaXJvbi1yZWxhdGVkIGdlbmUgc2V0cyB3ZXJlIGVucmljaGVkIGluIG91ciBERSByZXN1bHRzLgoKYGBge3IsIGV2YWw9RkFMU0V9CnNvdXJjZShoZXJlKCJSIiwiR1NFQSIsImNvbWJpbmVkR1NFQS5SIikpCgpnc2VhUmVzdWx0c19pcm9uU2V0cyA8LSBjb21iaW5lZEdTRUEodiwgaXJvblNldHNfaWR4LCBkZXNpZ24sIGNvbnRyYXN0cykKIyBnc2VhUmVzdWx0c19pcm9uU2V0cyAlPiUgc2F2ZVJEUyhoZXJlKCJSIiwiR1NFQSIsInJlc3VsdHMiLCJnc2VhUmVzdWx0c19pcm9uU2V0cy5yZHMiKSkKYGBgCgpSZXN1bHRzIGZvciAqKjYgbW9udGgsIG5vcm1veGlhLCBtdXRhbnQgdnMuIHdpbGQgdHlwZSoqIGJlbG93IGluZGljYXRlIHRoYXQgd2hpbGUgc29tZSBpcm9uLXJlbGF0ZWQgZ2VuZSBzZXRzIHNob3cgZW5yaWNobWVudCBpbiB0aGlzIGNvbXBhcmlzb24sIG90aGVycyBkbyBub3QuIApgYGB7ciBldmFsPUZBTFNFfQpnc2VhUmVzdWx0c19pcm9uU2V0cyRjb21iVGVzdCRub3Jtb3hpYV82bXRoX211dGFudF92c193dApgYGAKCk92ZXJhbGwsIHRoZSBmaWd1cmUgYmVsb3cgaW5kaWNhdGVzIHBvb3Igb3ZlcmxhcCBiZXR3ZWVuIGV4aXN0aW5nIGdlbmUgc2V0cyBhbmQgb3VyIElSRS1jb250YWluaW5nIGdlbmUgc2V0cy4gSG93ZXZlciwgdG8gcmVhbGx5IHNlZSBob3cgb3VyIElSRSBnZW5lc2V0cyByZWxhdGUgdG8gZXhpc3RpbmcgZ2VuZSBzZXRzLCB3ZSBuZWVkIHRvIGRvIGEgbW9yZSBjb21wcmVoZW5zaXZlIHRlc3QgaW5jbHVkaW5nIG1vcmUgb2YgdGhlIGdlbmUgc2V0cyBub3QgcmVzdHJpY3RlZCB0byBvbmVzIHJlbGF0ZWQgdG8gaXJvbiBtZXRhYm9saXNtLiAKCmBgYHtyIGZpZy53aWR0aD0xMSwgZmlnLmNhcD0iQ29tcGFyaXNvbiBvZiBnZW5lIG92ZXJsYXAgYmV0d2VlbiBvdXIgSVJFIGdlbmUgc2V0cyBhbmQgZXhpc3RpbmcgZ2VuZSBzZXRzIHJlbGF0ZWQgdG8gaXJvbiBtZXRhYm9saXNtLiJ9CiMgQ3JlYXRlIGEgZGF0YWZyYW1lIHRoYXQgaW5jbHVkZXMgb3ZlcmxhcCBiZXR3ZWVuIGlyb24tcmVsYXRlZCBnZW5lIHNldHMgYW5kIHRoZSBpcmVHZW5lcy4KdGVzdHggPC0gaXJvblNldHMgJT4lIGxhcHBseShmdW5jdGlvbih4KXsKICBvdmVybGFwIDwtIGxpc3QoCiAgd2l0aDNpcmUgPSBzdW0oeCAlaW4lIGlyZUdlbmVzJGlyZTNfYWxsKSwKICB3aXRoNWlyZSA9IHN1bSh4ICVpbiUgaXJlR2VuZXMkaXJlNV9hbGwpLAogIG5vSXJlID0gc3VtKCEoeCAlaW4lIGlyZUdlbmVzJGlyZTNfYWxsIHwgeCAlaW4lIGlyZUdlbmVzJGlyZTVfYWxsKSkKICApCn0pICU+JSBsYXBwbHkoZnVuY3Rpb24oeCl7CiAgICB4ICU+JSBiaW5kX3Jvd3MKfSkgJT4lIAogIGRvLmNhbGwoInJiaW5kIiwuKSAlPiUgCiAgbXV0YXRlKGdlbmVzZXQgPSBuYW1lcyhpcm9uU2V0cykpICU+JQogIGJpbmRfcm93cyhkYXRhLmZyYW1lKAogICAgd2l0aDNpcmUgPSBsZW5ndGgoaXJlR2VuZXMkaXJlM19hbGwpLAogICAgd2l0aDVpcmUgPSBsZW5ndGgoaXJlR2VuZXMkaXJlNV9hbGwpLAogICAgbm9JcmUgPSAwLAogICAgZ2VuZXNldCA9ICItIEdSQ3oxMSBaZWJyYWZpc2ggR2VuZXMgLSBBbGwgUHJlZGljdGVkIElSRXMiCiAgKSkgJT4lIAogIGJpbmRfcm93cyhkYXRhLmZyYW1lKAogICAgd2l0aDNpcmUgPSBpcmVVdHIzICU+JSBhcy5kYXRhLmZyYW1lICU+JSBkcGx5cjo6ZmlsdGVyKHF1YWxpdHkgPT0gIkhpZ2giKSAlPiUgdXNlX3NlcmllcygiZ2VuZV9pZCIpICU+JSB1bmlxdWUgJT4lIGxlbmd0aCwKICAgIHdpdGg1aXJlID0gaXJlVXRyNSAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgZHBseXI6OmZpbHRlcihxdWFsaXR5ID09ICJIaWdoIikgJT4lIHVzZV9zZXJpZXMoImdlbmVfaWQiKSAlPiUgdW5pcXVlICU+JSBsZW5ndGgsCiAgICBub0lyZSA9IDAsCiAgICBnZW5lc2V0ID0gIi0gR1JDejExIFplYnJhZmlzaCBHZW5lcyAtIEhpZ2ggUXVhbGl0eSBQcmVkaWN0ZWQgSVJFcyIKICApKSAlPiUKICBtdXRhdGUoZ2VuZXNldCA9IGdzdWIoeCA9IGdlbmVzZXQsIHBhdHRlcm4gPSAiXyIsIHJlcGxhY2VtZW50ID0gIiAiKSklPiUKICBtZWx0IAoKIyBDcmVhdGUgYSBzdGFja2VkIGJhciBjaGFydApjb21wUGxvdCA8LSB0ZXN0eCAlPiUgZ2dwbG90KGFlcyh4ID0gc3RyX3RvX3RpdGxlKHN0cmluZ3I6OnN0cl93cmFwKGdlbmVzZXQsIDIwKSksIAogICAgICAgICAgICAgICAgICAgICB5ID0gdmFsdWUsIAogICAgICAgICAgICAgICAgICAgICBmaWxsID0gdmFyaWFibGUpKSArIAogIGdlb21fY29sKCkgKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDAuNywgCiAgICAgICAgYXhpcy50ZXh0LnggPSAgZWxlbWVudF90ZXh0KHNpemU9IDM1KSwgCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9IDMwKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMwKSwgCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwgCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MzApLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImdyZXkyMCIsIHNpemUgPSAxNikpICsgCiAgY29vcmRfZmxpcCgpICsgICMgQXhpcyBsYWJlbHMgdG9vIGxvbmcgdG8gYmUgcmVhZGFibGUgc28gdGhpcyBoZWxwcy4KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiI0ZCQjgyOSIsICIjRkYwMDY2IiwgIiNkZGRkZGQiKSwgbGFiZWxzID1jKCIzJyBJUkUiLCAiNScgSVJFIiwgIk5vIElSRSIpKSArCiAgbGFicyh5ID0gIk51bWJlciBvZiBnZW5lcyIsIHggPSAiR2VuZSBzZXQiLCBmaWxsID0gIkdlbmVcbmhhc1xuSVJFPyIpIAoKY29tcFBsb3QKCmBgYAoKCgoKCiMjIDEuIERlZmluaW5nIFJlbGV2YW50IEdlbmUgU2V0cwoKV2Ugd2lsbCBiZSB0ZXN0aW5nIHdoZXRoZXIgb3VyIElSRSBnZW5lIHNldHMgYXJlIGVucmljaGVkIGluIGFueSBvZiB0aGUgZ2VuZSBzZXRzIGluIHRoZSBmb2xsb3dpbmcgZ2VuZSBzZXQgY29sbGVjdGlvbnMgZnJvbSAqKk1TaWdEQioqOgoKLSAqKkhhbGxtYXJrIGdlbmUgc2V0cyoqICgqKkgqKikgYXJlIGNvaGVyZW50bHkgZXhwcmVzc2VkIHNpZ25hdHVyZXMgZGVyaXZlZCBieSBhZ2dyZWdhdGluZyBtYW55IE1TaWdEQiBnZW5lIHNldHMgdG8gcmVwcmVzZW50IHdlbGwtZGVmaW5lZCBiaW9sb2dpY2FsIHN0YXRlcyBvciBwcm9jZXNzZXMuCi0gKipDdXJhdGVkIGdlbmUgc2V0cyoqICgqKkMyKiopIGFyZSBmcm9tIG9ubGluZSBwYXRod2F5IGRhdGFiYXNlcywgcHVibGljYXRpb25zIGluIFB1Yk1lZCwgYW5kIGtub3dsZWRnZSBvZiBkb21haW4gZXhwZXJ0cy4KLSAqKk1vdGlmIGdlbmUgc2V0cyoqICgqKkMzKiopIGFyZSBiYXNlZCBvbiBjb25zZXJ2ZWQgY2lzLXJlZ3VsYXRvcnkgbW90aWZzIGZyb20gYSBjb21wYXJhdGl2ZSBhbmFseXNpcyBvZiB0aGUgaHVtYW4sIG1vdXNlLCByYXQsIGFuZCBkb2cgZ2Vub21lcy4KLSAqKkdlbmUgb250b2xvZ3kgZ2VuZSBzZXRzKiogKCoqQzUqKikgY29uc2lzdCBvZiBnZW5lcyBhbm5vdGF0ZWQgYnkgdGhlIHNhbWUgR08gdGVybXMuIAoKVGhlIGdlbmUgc2V0IGNvbGxlY3Rpb25zIG1hcHBlZCB0byB6ZWJyYWZpc2ggRW5zZW1ibCBJRHMgaGF2ZSBiZWVuIHByZS1sb2FkZWQgZm9yIHRoaXMgYW5hbHlzaXMuIEVhY2ggY29sbGVjdGlvbiBpcyBhIG5hbWVkIGxpc3Qgb2YgZ2VuZSBzZXRzLCB3aXRoIGVhY2ggZ2VuZSBzZXQgY29udGFpbmluZyBFbnNlbWJsIElEcyBvZiBnZW5lcyBpbiB0aGF0IHNldC4gV2Ugd2lsbCBjb252ZXJ0IGFsbCBsaXN0cyBpbnRvIGxpc3QgY29sdW1uIHN0cnVjdHVyZXMgdXNlZCBpbiB0aGUgKnRpYmJsZSogcGFja2FnZS4gCgpgYGB7cn0KaF90aWIgPC0gaF9tYXBwZWQgJT4lIHRpYmJsZSAlPiUgc2V0X2NvbG5hbWVzKGMoImlkcyIpKSAlPiUgbXV0YXRlKGdlbmVzZXQgPSBuYW1lcyhoX21hcHBlZCksIHNvdXJjZSA9ICJoIikgCmMyX3RpYiA8LSBjMl9tYXBwZWQgJT4lIHRpYmJsZSAlPiUgc2V0X2NvbG5hbWVzKGMoImlkcyIpKSAlPiUgbXV0YXRlKGdlbmVzZXQgPSBuYW1lcyhjMl9tYXBwZWQpLCBzb3VyY2UgPSAiYzIiKQpjM190aWIgPC0gYzNfbWFwcGVkICU+JSB0aWJibGUgJT4lIHNldF9jb2xuYW1lcyhjKCJpZHMiKSkgJT4lIG11dGF0ZShnZW5lc2V0ID0gbmFtZXMoYzNfbWFwcGVkKSwgc291cmNlID0gImMzIikKYzVfdGliIDwtIGM1X21hcHBlZCAlPiUgdGliYmxlICU+JSBzZXRfY29sbmFtZXMoYygiaWRzIikpICU+JSBtdXRhdGUoZ2VuZXNldCA9IG5hbWVzKGM1X21hcHBlZCksIHNvdXJjZSA9ICJjNSIpCgpncyA8LSBiaW5kX3Jvd3MoaF90aWIsIGMyX3RpYiwgYzNfdGliLCBjNV90aWIpCgpncwpgYGAKCldlIHByZXZpb3VzbHkgbG9hZGVkIHRoZSBgaXJlR2VuZXNgIFIgb2JqZWN0LCB3aGljaCBoYXMgdGhlIGZvbGxvd2luZyBzdHJ1Y3R1cmU6CmBgYHtyfQppcmVHZW5lcyU+JXN0cgoKYWxsSVJFR2VuZXMgPC0gYyhpcmVHZW5lcyRpcmUzX2FsbCwgaXJlR2VuZXMkaXJlNV9hbGwpCgphbGxJUkVHZW5lcyAlPiUgaGVhZApgYGAKCiMjIDIuIENvbXB1dGUgZ2VuZSBzZXQgb3ZlcmxhcCB3aXRoIElSRSBnZW5lc2V0cwoKV2Ugd2lzaCB0byBzZWUgdGhlIG92ZXJsYXAgYmV0d2VlbiB0aGUgZ2VuZXNldHMgaW4gYGdzYCB3aXRoIHRoZSAKYGlyZUdlbmVzYCAoaW5jbHVkaW5nIDMnIGFuZCA1JyBJUkUgZ2VuZXMpLCAKYXMgd2VsbCBhcyBzZXBhcmF0ZWx5LCB3aXRoIHRoZSAzJyBJUkUgZ2VuZXMgYW5kIDUnIElSRSBnZW5lcy4gCldlIHdpbGwgYWRkIHRoaXMgaW5mb3JtYXRpb24gaW50byB0aGUgYGdzYCB0aWJibGUuCgpgYGB7cn0KZ3MgJTw+JSByb3d3aXNlKCkgJT4lIG11dGF0ZSgKICBuID0gbGVuZ3RoKGlkcyksICAjIE51bWJlciBvZiBnZW5lcyBpbiB0aGUgZ2VuZXNldAogIAogIG5fd2l0aF9pcmUgPSBzdW0oaWRzICVpbiUgYWxsSVJFR2VuZXMpLCAgIyBOdW1iZXIgb2YgZ2VuZXMgaW4gdGhlIGdlbmUgc2V0IHdoaWNoIGhhdmUgMycgb3IgNScgcHJlZGljdGVkIElSRXMgCiAgbl93aXRob3V0X2lyZSA9IChuIC0gbl93aXRoX2lyZSksCiAgdW5pdmVyc2Vfd2l0aF9pcmUgPSBzdW0ocm93bmFtZXModikgJWluJSBhbGxJUkVHZW5lcyAmICEocm93bmFtZXModikgJWluJSBpZHMpKSwgCiAgdW5pdmVyc2Vfd2l0aG91dF9pcmUgPSAobGVuZ3RoKHJvd25hbWVzKHYpKSAtIHVuaXZlcnNlX3dpdGhfaXJlKSwKICAKICBuX3dpdGhfaXJlMyA9IHN1bShpZHMgJWluJSBpcmVHZW5lcyRpcmUzX2FsbCksICAjIE51bWJlciBvZiBnZW5lcyBpbiB0aGUgZ2VuZSBzZXQgd2hpY2ggaGF2ZSAzJyBwcmVkaWN0ZWQgSVJFcwogIG5fd2l0aG91dF9pcmUzPSAobiAtIG5fd2l0aF9pcmUpLAogIHVuaXZlcnNlX3dpdGhfaXJlMyA9IHN1bShyb3duYW1lcyh2KSAlaW4lIGlyZUdlbmVzJGlyZTNfYWxsICYgIShyb3duYW1lcyh2KSAlaW4lIGlkcykpLCAKICB1bml2ZXJzZV93aXRob3V0X2lyZTMgPSAobGVuZ3RoKHJvd25hbWVzKHYpKSAtIHVuaXZlcnNlX3dpdGhfaXJlKSwKICAKICBuX3dpdGhfaXJlNSA9IHN1bShpZHMgJWluJSBpcmVHZW5lcyRpcmU1X2FsbCksICAjIE51bWJlciBvZiBnZW5lcyBpbiB0aGUgZ2VuZSBzZXQgd2hpY2ggaGF2ZSA1JyBwcmVkaWN0ZWQgSVJFcwogIG5fd2l0aG91dF9pcmU1PSAobiAtIG5fd2l0aF9pcmUpLAogIHVuaXZlcnNlX3dpdGhfaXJlNSA9IHN1bShyb3duYW1lcyh2KSAlaW4lIGlyZUdlbmVzJGlyZTVfYWxsICYgIShyb3duYW1lcyh2KSAlaW4lIGlkcykpLCAKICB1bml2ZXJzZV93aXRob3V0X2lyZTUgPSAobGVuZ3RoKHJvd25hbWVzKHYpKSAtIHVuaXZlcnNlX3dpdGhfaXJlKQopICU+JSB1bmdyb3VwKCkKCmdzCmBgYAoKIyMgMy4gQ29udGluZ2VuY3kgdGFibGVzCgpDcmVhdGUgY29udGluZ2VuY3kgdGFibGUgZm9yIGVhY2ggZ2VuZSBzZXQuIFdlIHdpbGwgc3RvcmUgdGhpcyBpbiBhIApsaXN0IG9mIG1hdHJpY2VzIGBnc19tYXRgLiAKVGhlIDMnIGFuZCA1JyBjb250aW5nZW5jeSBtYXRyaWNlcyBhcmUgc3RvcmVkIGluIGBnc19tYXQzYCBhbmQgYGdzX21hdDVgLiAKCmBgYHtyLCBldmFsPUZBTFNFfQpnc19tYXQgPC0gZ3MgJT4lIAogIGRwbHlyOjpzZWxlY3Qobl93aXRoX2lyZSwKICAgICAgICAgICAgICAgIG5fd2l0aG91dF9pcmUsIAogICAgICAgICAgICAgICAgdW5pdmVyc2Vfd2l0aF9pcmUsIAogICAgICAgICAgICAgICAgdW5pdmVyc2Vfd2l0aG91dF9pcmUpICU+JSAKICBhcHBseShYID0gLiwgTUFSR0lOID0gMSwgRlVOID0gZnVuY3Rpb24oeCl7CiAgICB4ICU+JSAKICAgICAgbWF0cml4KDIsMikgJT4lIAogICAgICB0ICU+JSAKICAgICAgbGlzdCgpCiAgfSkgJT4lIHNldF9uYW1lcyhncyRnZW5lc2V0KSAlPiUgCiAgbGFwcGx5KGZ1bmN0aW9uKHgpewogICAgeCAlPiUgLltbMV1dCiAgfSkKCmdzX21hdDMgPC0gZ3MgJT4lIAogIGRwbHlyOjpzZWxlY3Qobl93aXRoX2lyZTMsCiAgICAgICAgICAgICAgICBuX3dpdGhvdXRfaXJlMywgCiAgICAgICAgICAgICAgICB1bml2ZXJzZV93aXRoX2lyZTMsIAogICAgICAgICAgICAgICAgdW5pdmVyc2Vfd2l0aG91dF9pcmUzKSAlPiUgCiAgYXBwbHkoWCA9IC4sIE1BUkdJTiA9IDEsIEZVTiA9IGZ1bmN0aW9uKHgpewogICAgeCAlPiUgCiAgICAgIG1hdHJpeCgyLDIpICU+JSAKICAgICAgdCAlPiUgCiAgICAgIGxpc3QoKQogIH0pICU+JSBzZXRfbmFtZXMoZ3MkZ2VuZXNldCkgJT4lIAogIGxhcHBseShmdW5jdGlvbih4KXsKICAgIHggJT4lIC5bWzFdXQogIH0pCgpnc19tYXQ1IDwtIGdzICU+JSAKICBkcGx5cjo6c2VsZWN0KG5fd2l0aF9pcmU1LAogICAgICAgICAgICAgICAgbl93aXRob3V0X2lyZTUsIAogICAgICAgICAgICAgICAgdW5pdmVyc2Vfd2l0aF9pcmU1LCAKICAgICAgICAgICAgICAgIHVuaXZlcnNlX3dpdGhvdXRfaXJlNSkgJT4lIAogIGFwcGx5KFggPSAuLCBNQVJHSU4gPSAxLCBGVU4gPSBmdW5jdGlvbih4KXsKICAgIHggJT4lIAogICAgICBtYXRyaXgoMiwyKSAlPiUgCiAgICAgIHQgJT4lIAogICAgICBsaXN0KCkKICB9KSAlPiUgc2V0X25hbWVzKGdzJGdlbmVzZXQpICU+JSAKICBsYXBwbHkoZnVuY3Rpb24oeCl7CiAgICB4ICU+JSAuW1sxXV0KICB9KQoKYGBgCmBgYHtyfQpnc19tYXRbMTozXQpnc19tYXQzWzE6M10KZ3NfbWF0NVsxOjNdCmBgYAoKCiMjIDQuIEZpc2hlcidzIGV4YWN0IHRlc3QKCk9uIGVhY2ggY29udGluZ2VuY3kgdGFibGUsIHdlIHdpbGwgcnVuIEZpc2hlcidzIGV4YWN0IHRlc3QuIApXZSB0aGVuIGFwcGx5IEZEUiBjb3JyZWN0aW9uIHRvIGFkanVzdCB0aGUgcmF3ICpwKi12YWx1ZXMgZm9yIG11bHRpcGxlIHRlc3RpbmcuIAoKYGBge3IsZXZhbD1GQUxTRX0KZmlzaGVyX3JlcyA8LSBnc19tYXQgJT4lIGxhcHBseShmdW5jdGlvbih4KXsKICB4ICU+JSBmaXNoZXIudGVzdCgpCn0pCgpmaXNoZXJfcmVzMyA8LSBnc19tYXQzICU+JSBsYXBwbHkoZnVuY3Rpb24oeCl7CiAgeCAlPiUgZmlzaGVyLnRlc3QoKQp9KQoKZmlzaGVyX3JlczUgPC0gZ3NfbWF0NSAlPiUgbGFwcGx5KGZ1bmN0aW9uKHgpewogIHggJT4lIGZpc2hlci50ZXN0KCkKfSkKCmZpc2hlcl9yZXNfcCA8LSBmaXNoZXJfcmVzICU+JSBsYXBwbHkoZnVuY3Rpb24oeCl7eCRwLnZhbHVlfSkKZmlzaGVyX3Jlc19wMyA8LSBmaXNoZXJfcmVzMyAlPiUgbGFwcGx5KGZ1bmN0aW9uKHgpe3gkcC52YWx1ZX0pCmZpc2hlcl9yZXNfcDUgPC0gZmlzaGVyX3JlczUgJT4lIGxhcHBseShmdW5jdGlvbih4KXt4JHAudmFsdWV9KQoKZ3MgJTw+JSAKICBtdXRhdGUoZmlzaGVyX3AgPSBmaXNoZXJfcmVzX3AlPiV1bmxpc3QlPiV1bm5hbWUsCiAgICAgICAgIGZpc2hlcl9wXzMgPSBmaXNoZXJfcmVzX3AzJT4ldW5saXN0JT4ldW5uYW1lLAogICAgICAgICBmaXNoZXJfcF81ID0gZmlzaGVyX3Jlc19wNSU+JXVubGlzdCU+JXVubmFtZSkgJT4lCiAgbXV0YXRlKGZkciA9IHAuYWRqdXN0KGZpc2hlcl9wLCAiZmRyIiksCiAgICAgICAgIGZkcl8zID0gcC5hZGp1c3QoZmlzaGVyX3BfMywgImZkciIpLAogICAgICAgICBmZHJfNSA9IHAuYWRqdXN0KGZpc2hlcl9wXzUsICJmZHIiKSkKCmBgYAoKCiMjIDUuIENhbGN1bGF0ZSBFeHBlY3RlZCBhbmQgT2JzZXJ2ZWQKCkZvciBlYWNoIGdlbmUgc2V0LCB3ZSB3aWxsIGNhbGN1bGF0ZSB0aGUgbnVtYmVyIG9mIGdlbmVzICoqZXhwZWN0ZWQqKiB0byBoYXZlIApJUkVzIChiYXNlZCBvbiB0aGUgYmFja2dyb3VuZCBwcm9wb3J0aW9uKSBhbmQgKipvYnNlcnZlZCoqIHZhbHVlLiAKVGhpcyBpbmZvcm1hdGlvbiB3aWxsIGJlIGFkZGVkIHRvIHRoZSBgZ3NgIG9iamVjdC4KCmBgYHtyIGV2YWw9RkFMU0V9CmdzICU8PiUgcm93d2lzZSgpICU+JSBtdXRhdGUoCiAgZXhwX2FsbElSRSA9ICh1bml2ZXJzZV93aXRoX2lyZSAvIHVuaXZlcnNlX3dpdGhvdXRfaXJlKSpuX3dpdGhvdXRfaXJlLAogIG9ic19hbGxJUkUgPSBuX3dpdGhfaXJlLAogIG9ic19ncmVhdGVyX3RoYW5fZXhwX2FsbElSRSA9IG9ic19hbGxJUkUgPiBleHBfYWxsSVJFLAogIAogIGV4cF9pcmUzID0gKHVuaXZlcnNlX3dpdGhfaXJlMyAvIHVuaXZlcnNlX3dpdGhvdXRfaXJlMykqbl93aXRob3V0X2lyZTMsCiAgb2JzX2lyZTMgPSBuX3dpdGhfaXJlMywKICBvYnNfZ3JlYXRlcl90aGFuX2V4cF9pcmUzID0gb2JzX2lyZTMgPiBleHBfaXJlMywKICAKICBleHBfaXJlNSA9ICh1bml2ZXJzZV93aXRoX2lyZTUgLyB1bml2ZXJzZV93aXRob3V0X2lyZTUpKm5fd2l0aG91dF9pcmU1LAogIG9ic19pcmU1ID0gbl93aXRoX2lyZTUsCiAgb2JzX2dyZWF0ZXJfdGhhbl9leHBfaXJlNSA9IG9ic19pcmU1ID4gZXhwX2lyZTUKKSAKYGBgCgpgYGB7cn0KZ3MgJT4lCiAgZHBseXI6OnNlbGVjdChnZW5lc2V0LCBjb250YWlucygiZXhwIiksIHN0YXJ0c193aXRoKCJvYnMiKSwgKSAlPiUgdW5ncm91cCgpCmBgYAoKCiMjIDYuIFJlc3VsdHMKClRoZSBmb2xsb3dpbmcgdGFibGUgc2hvd3MgdGhlIHRvcCA1MCBnZW5lIHNldHMgZW5yaWNoZWQgaW4gSVJFIGdlbmVzZXRzLCAKcmFua2VkIGJ5IEZpc2hlcidzIGV4YWN0IHRlc3QgKnAqLXZhbHVlczoKCmBgYHtyfQpncyAlPiUgdW5ncm91cCgpICU+JQogIGFycmFuZ2UoZmlzaGVyX3ApICU+JQogIGRwbHlyOjpzZWxlY3QoZ2VuZXNldCwgY29udGFpbnMoImZkciIpLCBuX3dpdGhfaXJlLCBuLCBzdGFydHNfd2l0aCgib2JzX2dyZWF0ZXIiKSkgJT4lIAogIGhlYWQoNTApCmBgYAoKU29ydGVkIGJ5IGdlbmVzZXRzIG1vc3QgZW5yaWNoZWQgaW4gMycgSVJFIGdlbmVzOgpgYGB7cn0KZ3MgJT4lIHVuZ3JvdXAgJT4lIGFycmFuZ2UoZmlzaGVyX3BfMykgJT4lIGRwbHlyOjpzZWxlY3QoZ2VuZXNldCwgY29udGFpbnMoImZkciIpLCBuX3dpdGhfaXJlMywgbiwgc3RhcnRzX3dpdGgoIm9ic19ncmVhdGVyIikpICU+JSBoZWFkKDUwKQpgYGAKClNvcnRlZCBieSBnZW5lc2V0cyBtb3N0IGVucmljaGVkIGluIDUnIElSRSBnZW5lczoKYGBge3J9CmdzICU+JSB1bmdyb3VwICU+JSBhcnJhbmdlKGZpc2hlcl9wXzUpICU+JSBkcGx5cjo6c2VsZWN0KGdlbmVzZXQsIGNvbnRhaW5zKCJmZHIiKSwgbl93aXRoX2lyZTUsIG4sIHN0YXJ0c193aXRoKCJvYnNfZ3JlYXRlciIpKSAlPiUgaGVhZCg1MCkKYGBgCgoKIyMgNy4gVmlzdWFsaXNhdGlvbiAoU3RhY2tlZCBiYXIgY2hhcnQpCgpUaGUgZm9sbG93aW5nIHN0YWNrZWQgYmFyIGNoYXJ0IHNob3dzIHRoZSBvdmVybGFwIGJldHdlZW4gdGhlIHRvcCB+MjAgCmdlbmVzZXRzIGFuZCB0aGUgcHJlZGljdGVkLUlSRSBnZW5lc2V0cy4KCmBgYHtyIGZpZy53aWR0aD0xMSxmaWcuaGVpZ2h0PTh9Cm92ZXJsYXBEZiA8LSBncyAlPiUgYXJyYW5nZShmaXNoZXJfcCkgJT4lIGRwbHlyOjpmaWx0ZXIoZmRyIDwgMC4xIHwgZmRyXzMgPCAwLjEgfCBmZHJfNSA8IDAuMSkgJT4lCiAgZHBseXI6OmZpbHRlcihvYnNfZ3JlYXRlcl90aGFuX2V4cF9hbGxJUkUgPT0gVFJVRSB8IG9ic19ncmVhdGVyX3RoYW5fZXhwX2lyZTMgPT0gVFJVRSB8IG9ic19ncmVhdGVyX3RoYW5fZXhwX2lyZTUgPT0gVFJVRSkgJT4lCiAgZHBseXI6OnNlbGVjdChnZW5lc2V0LCBzb3VyY2UsIG5fd2l0aF9pcmUzLCBuX3dpdGhfaXJlNSwgbl93aXRob3V0X2lyZSkgJT4lCiAgYmluZF9yb3dzKGRhdGEuZnJhbWUoCiAgICBnZW5lc2V0ID0gYygiLSBHUkN6MTEgWmVicmFmaXNoIEdlbmVzIC0gQWxsIFByZWRpY3RlZCBJUkVzIiksCiAgICBzb3VyY2UgPSBjKCJzaXJlcyIpLAogICAgbl93aXRoX2lyZTMgPSBjKGxlbmd0aChpcmVHZW5lcyRpcmUzX2FsbCkpLAojICAgICAgICAgICAgICAgICAgICBpcmVVdHIzICU+JSBhcy5kYXRhLmZyYW1lICU+JSBkcGx5cjo6ZmlsdGVyKHF1YWxpdHkgPT0gIkhpZ2giKSAlPiUgdXNlX3NlcmllcygiZ2VuZV9pZCIpICU+JSB1bmlxdWUgJT4lIGxlbmd0aCksCiAgICBuX3dpdGhfaXJlNSA9IGMobGVuZ3RoKGlyZUdlbmVzJGlyZTVfYWxsKSksCiMgICAgICAgICAgICAgICAgICAgIGlyZVV0cjUgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIGRwbHlyOjpmaWx0ZXIocXVhbGl0eSA9PSAiSGlnaCIpICU+JSB1c2Vfc2VyaWVzKCJnZW5lX2lkIikgJT4lIHVuaXF1ZSAlPiUgbGVuZ3RoKSwKICAgIG5fd2l0aG91dF9pcmUgPSBjKDApCiAgKSkgJT4lIGRwbHlyOjptdXRhdGUoZ2VuZXNldCA9IHBhc3RlMChnZW5lc2V0LCAiICIsIHNvdXJjZSkpICU+JQogIGRwbHlyOjptdXRhdGUoZ2VuZXNldCA9IGdzdWIoeD1nZW5lc2V0LCBwYXR0ZXJuID0gIl8iLCByZXBsYWNlbWVudCA9ICIgIikpICU+JQogIGRwbHlyOjpzZWxlY3QoLXNvdXJjZSkgJT4lIG1lbHQKCm92ZXJsYXBQbG90IDwtIG92ZXJsYXBEZiAlPiUgZ2dwbG90KGFlcyh4ID0gc3RyX3RvX3RpdGxlKHN0cmluZ3I6OnN0cl93cmFwKGdlbmVzZXQsIDIzKSksIAogICAgICAgICAgICAgICAgICAgICB5ID0gdmFsdWUsIAogICAgICAgICAgICAgICAgICAgICBmaWxsID0gdmFyaWFibGUpKSArIAogIGdlb21fY29sKCkgKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDAuNywgCiAgICAgICAgYXhpcy50ZXh0LnggPSAgZWxlbWVudF90ZXh0KHNpemU9IDM1KSwgCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9IDMwKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMwKSwgCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwgCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MzApLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImdyZXkyMCIsIHNpemUgPSAxMykpICsgCiAgY29vcmRfZmxpcCgpICsgICMgQXhpcyBsYWJlbHMgdG9vIGxvbmcgdG8gYmUgcmVhZGFibGUgc28gdGhpcyBoZWxwcy4KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiI0ZCQjgyOSIsICIjRkYwMDY2IiwgIiNkZGRkZGQiKSwgbGFiZWxzID1jKCIzJyBJUkUiLCAiNScgSVJFIiwgIk5vIElSRSIpKSArCiAgbGFicyh5ID0gIk51bWJlciBvZiBnZW5lcyIsIHggPSAiR2VuZSBzZXQiLCBmaWxsID0gIkdlbmVcbmhhc1xuSVJFPyIpIAoKb3ZlcmxhcFBsb3QKCiMgZXhwb3J0OjpncmFwaDJwcHQob3ZlcmxhcFBsb3QsIGhlcmUoIlIiLCJHU0VBIiwiZmlnIiwib3ZlcmxhcFBsb3QiKSkKYGBgCgojIyA4LiBWaXN1YWxpc2F0aW9uIChOZXR3b3JrKQoKQWx0aG91Z2ggdGhlIHN0YWNrZWQgYmFyIGNoYXJ0IHNob3dzIG92ZXJsYXAgYmV0d2VlbiBkaWZmZXJlbnQgZ2VuZSBzZXRzIHdpdGggdGhlIHByZWRpY3RlZCBJUkUgZ2VuZXNldCwgYWxvbmcgd2l0aCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc2l6ZSBvZiBlYWNoIGdlbmVzZXQsIGNydWNpYWxseSwgaXQgZG9lc24ndCBpbmRpY2F0ZSB3aGljaCBnZW5lcyBhcmUgaW4gY29tbW9uIGJldHdlZW4gdGhlIGRpZmZlcmVudCBnZW5lIHNldHMuIE15IGZpcnN0IGlkZWEgd2FzIHBhaXJ3aXNlIFZlbm4gZGlhZ3JhbXMgYmV0d2VlbiB0aGUgSVJFIGdlbmVzIGluIGVhY2ggcGFpciBvZiBnZW5lc2V0cywgYnV0IHRoaXMgaXNuJ3QgdGhlIG1vc3QgdmlzdWFsIC8gaW50dWl0aXZlIHdheSB0byBwcmVzZW50IHRoZSBpbmZvcm1hdGlvbiBhbmQgaXQgZG9lc24ndCBoaWdobGlnaHQgdGhlIGdlbmVzIHdoaWNoIGFyZSBtb3N0IG9mdGVuIHNoYXJlZCBiZXR3ZWVuIGRpZmZlcmVudCBnZW5lc2V0cy4gU3RldmUgaGFkIHRoZSBpZGVhIHRvIHJlcHJlc2VudCB0aGlzIGFzIGEgbmV0d29yaywgd2l0aCBkaXN0aW5jdCBncm91cHMgb2YgZ2VuZXMgKGdyZXkpIHJlcHJlc2VudGluZyBnZW5lc2V0cywgSVJFIGdlbmVzIGluIGNvbG91ciwgYW5kIGVkZ2VzIHJlcHJlc2VudGluZyBvdmVybGFwIGJldHdlZW4gZ2VuZXNldHMuIAoKCiMjIyA4LjEuIENyZWF0ZSB0aGUgbm9kZXMgdGFibGUKCkkgd2lsbCBuZWVkIHRvIGNyZWF0ZSBhIGBkYXRhLmZyYW1lYCB3aXRoIHRoZSBmb2xsb3dpbmcgY29sdW1uczoKCi0gKipJZCoqOiBHZW5lIElECi0gKipJUkUqKjogRmFjdG9yIHdoaWNoIGNhbiBlaXRoZXIgYmUgYDMnIElSRWAsIGA1JyBJUkVgLCBvciBgbm8gSVJFYC4gCi0gKipHZW5lc2V0Kio6IFRoZSBnZW5lIHNldCB3aGljaCBpdCBiZWxvbmdzIHRvLiAKCmBgYHtyfQpub2RlczwtIGdzICU+JSBhcnJhbmdlKGZpc2hlcl9wKSAlPiUgZHBseXI6OmZpbHRlcihmZHIgPCAwLjEgfCBmZHJfMyA8IDAuMSB8IGZkcl81IDwgMC4xKSAlPiUKICBkcGx5cjo6ZmlsdGVyKG9ic19ncmVhdGVyX3RoYW5fZXhwX2FsbElSRSA9PSBUUlVFIHwgb2JzX2dyZWF0ZXJfdGhhbl9leHBfaXJlMyA9PSBUUlVFIHwgb2JzX2dyZWF0ZXJfdGhhbl9leHBfaXJlNSA9PSBUUlVFKSAlPiUKICB1bmdyb3VwCgojIEFwcGVuZCBpbmZvcm1hdGlvbiBhYm91dCAzJyBhbmQgNScgSVJFIGdlbmVzZXRzIApub2RlcyAlPD4lIGJpbmRfcm93cygKICAgdGliYmxlKAogICAgIGlkcyA9IGMobGlzdChpcmVHZW5lcyRpcmUzX2FsbCwgaXJlR2VuZXMkaXJlNV9hbGwpKSwKICAgICBnZW5lc2V0ID0gYygiUHJlZGljdGVkIDMnIElSRSBnZW5lcyIsICJQcmVkaWN0ZWQgNScgSVJFIGdlbmVzIiksCiAgICAgc291cmNlID0gYygic2lyZXMiLCJzaXJlcyIpLAogICAgIG4gPSBjKGxlbmd0aChpcmVHZW5lcyRpcmUzX2FsbCksIGxlbmd0aChpcmVHZW5lcyRpcmU1X2FsbCkpCiAgICkKICkgCgojIEFwcGVuZCB0aGUgSGFsbG1hcmsgSGVtZSBNZXRhYm9saXNtIGdlbmVzZXQKIyBBbHRob3VnaCB0aGlzIGdlbmUgc2V0IHdhc250IHNpZ25pZmljYW50bHkgZW5yaWNoZWQgaW4gCiMgSVJFLWNvbnRhaW5pbmcgZ2VuZXMsIGl0IGlzIHByb2JhYmx5IHRoZSBtb3N0IGNvbXByZWhlbnNpdmUKIyBnZW5lIHNldCBzcGVjaWZpY2FsbHkgb24gaGVtZSBtZXRhYm9saXNtLCBhbmQgY29tYmluZXMgaW5mbyAKIyBmcm9tIHZhcmlvdXMgc3R1ZGllcy4gCm5vZGVzICU8PiUgYmluZF9yb3dzKAogIGhfdGliICU+JSBmaWx0ZXIoZ2VuZXNldCA9PSAiSEFMTE1BUktfSEVNRV9NRVRBQk9MSVNNIikgJT4lIG11dGF0ZShuID0gbGVuZ3RoKGlkc1tbMV1dKSkKICkgCgpub2RlcwpgYGAKCkZpcnN0IHdlIGZpbHRlcmVkIGBnc2AgZm9yIG9ubHkgdGhlIGdlbmVzZXRzIHRoYXQgYXJlIGhpZ2hseSByYW5rZWQgaW4gYmVpbmcgZW5yaWNoZWQgZm9yIHByZWRpY3RlZCBJUkUgZ2VuZXMuIFdlIGFwcGxpZWQgdGhlIGFkZGl0aW9uYWwgZmlsdGVyaW5nIHN0ZXAgdGhhdCB0aGUgYG9ic2VydmVkYCBudW1iZXIgb2YgSVJFIGdlbmVzIGlzIGdyZWF0ZXIgdGhhbiB0aGUgYGV4cGVjdGVkYCBudW1iZXIuIFRoaXMgcmVzdWx0cyBpbiBgciBucm93KG5vZGVzKWAgZ2VuZXNldHMuIAoKVGhlIG5leHQgc3RlcCBpcyB0byBjcmVhdGUgYWxsIHBvc3NpYmxlIGNvbWJpbmF0aW9ucyBvZiB0aGUgZ2VuZXNldHMuIFdlIHdpbGwgdXNlIHRoZSBgY29tYm5gIGZ1bmN0aW9uIHRvIGRvIHRoaXMuIFRoZW4gd2Ugd2lsbCBjcmVhdGUgYSBjb2x1bW4gYGNvbW1vbl9pZHNgIHRvIHN0b3JlIHRoZSBnZW5lcyB3aGljaCBhcmUgY29tbW9uIHRvIGJvdGggZ2VuZSBzZXRzIGJlaW5nIGludGVyc2VjdGVkLiAKCmBgYHtyfQpnc0NvbWIgPC0gY29tYm4obm9kZXMkZ2VuZXNldCwgbSA9IDIpICU+JSB0ICU+JSBhcy5kYXRhLmZyYW1lICU+JQogIHNldF9jb2xuYW1lcyhjKCJnZW5lc2V0IiwgImdlbmVzZXRfMmIiKSkgJT4lCiAgbGVmdF9qb2luKG5vZGVzJT4lZHBseXI6OnNlbGVjdChpZHMsIGdlbmVzZXQpLCBieSA9ICJnZW5lc2V0IikgJT4lCiAgZHBseXI6OnJlbmFtZShnZW5lc2V0X25tID0gZ2VuZXNldCwKICAgICAgICAgICAgICAgIGdlbmVzZXQgPSBnZW5lc2V0XzJiKSAlPiUKICBsZWZ0X2pvaW4obm9kZXMlPiVkcGx5cjo6c2VsZWN0KGlkcywgZ2VuZXNldCksIGJ5ID0gImdlbmVzZXQiKSAlPiUKICBhc190aWJibGUgJT4lCiAgbXV0YXRlKGNvbW1vbl9pZHMgPSBtYXAyKGlkcy54LCBpZHMueSwgfmludGVyc2VjdCgueCwueSkpKQoKZ3NDb21iIApgYGAKCldlIGNhbiBleHRyYWN0IGdlbmVzIHdoaWNoIGFwcGVhciB0aGUgbW9zdCBvZnRlbiBpbiBnZW5lc2V0cyBhcyBmb2xsb3dzOgoKYGBge3J9CmdzQ29tYiRjb21tb25faWRzJT4ldW5saXN0ICU+JSB0YWJsZSAlPiUgYXMuZGF0YS5mcmFtZSU+JSBhcnJhbmdlKGRlc2MoRnJlcSkpICU+JSBzZXRfY29sbmFtZXMoYygiZW5zZW1ibF9nZW5lX2lkIiwgImYiKSkgJT4lIGxlZnRfam9pbih2JGdlbmVzICU+JSBkcGx5cjo6c2VsZWN0KC1lbnRyZXppZCkpICU+JSBhc190aWJibGUKYGBgCgpUaGUgbm9kZXMgdGFibGUgd2lsbCBjb250YWluCgotIEFsbCBnZW5lcyBpbiB0aGUgZ2VuZXNldHMKLSBUaGUgZ2VuZXNldCBuYW1lcwoKYGBge3J9CnBhdGh3YXlzIDwtIG5vZGVzJGdlbmVzZXQgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIHNldF9jb2xuYW1lcygibGFiZWwiKQpnZW5lcyA8LSBub2RlcyRpZHMgJT4lIHVubGlzdCAlPiUgdW5pcXVlICU+JSBhcy5kYXRhLmZyYW1lICU+JSBzZXRfY29sbmFtZXMoImxhYmVsIikKbm9kZXNEZiA8LSBmdWxsX2pvaW4ocGF0aHdheXMsIGdlbmVzLCBieSA9ICJsYWJlbCIpICU+JSByb3dpZF90b19jb2x1bW4oImlkIikKCiMgbm9kZXNEZiAlPiUKIyAgIG11dGF0ZSh0ZXh0ID0gaWZlbHNlKGlkIDwgMTgsIGxhYmVsLCBOQSkpICU+JQojICAgbXV0YXRlKHNpemUgPSBpZmVsc2UoaWQgPCAxNiwgMiwgMSkpICU+JQojICAgbXV0YXRlKGNvbG91ciA9IGlmZWxzZShpZCA8IDE2LCByYWluYm93KDI2KVtpZF0sIE5BKSkgCgpoZWFkKG5vZGVzRGYsMjApCgpub2Rlc0RmMiA8LSBub2Rlc0RmICU+JQogIG11dGF0ZSgKICAgIGlyZSA9IGNhc2Vfd2hlbigKICAgICAgaWQgPCAxNiB+ICIgIiwKICAgICAgaWQgPT0gMTggfiAiICIsCiAgICAgIGlkID09IDE2IH4gIjMiLAogICAgICBpZCA9PSAxNyB+ICI1IiwKICAgICAgbGFiZWwgJWluJSBpcmVHZW5lcyRpcmUzX2FsbCB+ICIzIiwKICAgICAgbGFiZWwgJWluJSBpcmVHZW5lcyRpcmU1X2FsbCB+ICI1IiwKICAgICAgIShsYWJlbCAlaW4lIGFsbElSRUdlbmVzKSB+ICJubyBJUkUiCiAgICApCiAgKSU+JQogIGRwbHlyOjpzZWxlY3QoLWlkKSAlPiUgZHBseXI6OnJlbmFtZShJZCA9IGxhYmVsKSAKCmhlYWQobm9kZXNEZjIsIDMwKSAKIyBub2Rlc0RmMiAlPiUgd3JpdGVfdHN2KGhlcmUoIlIiLCJHU0VBIiwiZGF0YSIsIm5vZGVzMi50c3YiKSkKYGBgCgpFZGdlcyB3aWxsIGJlIGJldHdlZW46CgotIFRoZSBnZW5lc2V0cyBhbmQgYWxsIGdlbmVzIHdpdGhpbiB0aGUgZ2VuZXNldCAKLSBHZW5lcyBpbiBtdWx0aXBsZSBnZW5lc2V0cwoKYGBge3J9CmVkZ2VEZiA8LSBub2RlcyRpZHMgJT4lIHNldF9uYW1lcyhub2RlcyRnZW5lc2V0KSAlPiUgcGx5cjo6bGRwbHkoZGF0YS5mcmFtZSkgJT4lIAogIHNldF9jb2xuYW1lcyhjKCJwYXRod2F5IiwgImdlbmUiKSkgJT4lCiAgbGVmdF9qb2luKG5vZGVzRGYsIGJ5ID0gYygicGF0aHdheSI9ImxhYmVsIikpICU+JQogIGRwbHlyOjpyZW5hbWUoZnJvbSA9IGlkKSAlPiUKICBsZWZ0X2pvaW4obm9kZXNEZiwgYnkgPSBjKCJnZW5lIj0ibGFiZWwiKSkgJT4lCiAgZHBseXI6OnJlbmFtZSh0bz1pZCkgJT4lCiAgI2RwbHlyOjpzZWxlY3QoZnJvbSwgdG8pCiAgZHBseXI6OnNlbGVjdChwYXRod2F5LCBnZW5lKSAlPiUKICBzZXRfY29sbmFtZXMoYygiU291cmNlIiwiVGFyZ2V0IikpCmVkZ2VEZiAlPiUgaGVhZCgyMCkKCgpgYGAKCgojIyBFeHBvcnQgcmVzdWx0cwoKYGBge3IgZXZhbD1GQUxTRX0KZ3MgJT4lIHNhdmVSRFMoaGVyZSgiUiIsIkdTRUEiLCJyZXN1bHRzIiwiZ3MucmRzIikpCmdzICU+JSB1bmdyb3VwKCkgJT4lIHdyaXRlLnhsc3goaGVyZSgiUiIsIkdTRUEiLCJyZXN1bHRzIiwiZ3MueGxzeCIpKQoKIyBFeHBvcnQgc2lnbmlmaWNhbnQgZ2VuZXNldHMgLyBvZiBpbnRlcmVzdApncyAlPiV1bmdyb3VwICU+JSAKICBhcnJhbmdlKGZpc2hlcl9wKSAlPiUKICBkcGx5cjo6ZmlsdGVyKGZkciA8IDAuMSB8IGZkcl8zIDwgMC4xIHwgZmRyXzUgPCAwLjEpICU+JQogIGRwbHlyOjpmaWx0ZXIob2JzX2dyZWF0ZXJfdGhhbl9leHBfYWxsSVJFID09IFRSVUUgfCBvYnNfZ3JlYXRlcl90aGFuX2V4cF9pcmUzID09IFRSVUUgfCBvYnNfZ3JlYXRlcl90aGFuX2V4cF9pcmU1ID09IFRSVUUpICU+JQogIGRwbHlyOjpzZWxlY3QoZ2VuZXNldCwgc291cmNlLCBuLCBuX3dpdGhfaXJlMywgbl93aXRoX2lyZTUsIG5fd2l0aG91dF9pcmUsIGNvbnRhaW5zKCJmaXNoZXJfcCIpLCBjb250YWlucygiZmRyIikpICU+JQogIHdyaXRlLnhsc3goaGVyZSgiUiIsIkdTRUEiLCJyZXN1bHRzIiwiZ3NfdG9wUmFua2VkLnhsc3giKSkKYGBgCgojIyA5LiBIdW1hbiBEYXRhCgotIE1hbmFnZWQgdG8gZ2V0IHRoZSBzZXRzIG9mIGh1bWFuIGdlbmVzIGNvbnRhaW5pbmcgSVJFczoKCmBgYHtyfQp6ZWJyYWZpc2hJcmVHZW5lcyA8LSByZWFkUkRTKGhlcmU6OmhlcmUoIlIvR1NFQS9kYXRhL2lyZUdlbmVzLnJkcyIpKQpodW1hbklyZUdlbmVzIDwtIHJlYWRSRFMoaGVyZTo6aGVyZSgiUi9JUkVHZW5lcy9kYXRhL2h1bWFuX2lyZUdlbmVzLnJkcyIpKQpgYGAKCgojIyBUT0RPCgotIFt4XSBGaXNoZXIncyB0ZXN0IHNlcGFyYXRlbHkgb24gMycgSVJFIGdlbmVzIGFuZCA1JyBJUkUgZ2VuZXMuIAotIFt4XSBTdGFja2VkIGJhciBjaGFydCB0byBpbmRpY2F0ZSBvdmVybGFwIG9mIHRoZSB0b3AgfjIwIGdlbmVzZXRzIHdpdGggcHJlZGljdGVkIElSRSBnZW5lcy4gCiAgLSBbIF0gUmVwZWF0IHRoaXMgYnV0IHdpdGggaHVtYW4gLyBtb3VzZSBJUkVzIGFuZCB0aGVpciBnZW5lc2V0cyB0byBkbyBhIGNyb3NzLXNwZWNpZXMgY29tcGFyaXNvbi4gCi0gW3hdIEFkZCBpbiBjYWxjdWxhdGlvbiBvZiBFeHBlY3RlZCBhbmQgT2JzZXJ2ZWQgSVJFIGdlbmUgdmFsdWVzIGFuZCBmaWx0ZXIgc2lnbmlmaWNhbnQgcmVzdWx0cyB0byBoYXZlIE9ic2VydmVkID4gRXhwZWN0ZWQuIAotIFt4XSBFeHBvcnQgbm9kZXMgYW5kIGVkZ2VzIHRhYmxlIGZvciBuZXR3b3JrIHZpc3VhbGlzYXRpb24uIAotIFt4XSBFeHBvcnQgZWRnZXMgYW5kIG5vZGVzIHRhYmxlIGFnYWluIGV4Y2VwdCB3aXRoIEhhbGxtYXJrIEhlbWUgbWV0YWJvbGlzbSBhZGRlZCBpbgoKIyMgU2Vzc2lvbiBJbmZvCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCg==